/*
 * =====================================================================================
 *       Filename:  http.c
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>
#include <stdarg.h>
#include <pthread.h>
#include "../global.h"
#include "../zipmap.h"
#include "../dict.h"
#include "http.h"

static void parseMUQ(const char* line, char* method, int method_size, char* uri, int uri_size, char* query, int query_size)
{
    int i, j;
    int size = method_size - 1;
    j = 0;
    for (i = 0; line[i] != ' ' && j < size; i++) {
        method[j++] = line[i];
    }
    method[j] = '\0';

    size = uri_size - 1;
    j = 0;
    for (i++; line[i] != ' ' && j < size; i++) {
        uri[j++] = line[i];
    }
    uri[j] = '\0';

    char* qmark = uri;
    while ((*qmark) != '?' && (*qmark != '\0'))
        qmark++;

    if (*qmark == '?') {
        *qmark = '\0';
        qmark++;
    }

    strncpy(query, qmark, query_size);

    if (strstr(uri, "..")) {
        sprintf(uri, "/");
    }
}

static void parseKV(const char* line, char* key, int key_size, char* value, int value_size)
{
    const char* p = line;
    int i = 0;
    while (1) {
        key[i] = *p;
        if (*p == ':') {
            key[i] = '\0';
            break;
        }
        if ((*p == '\n') || (*p == '\0') || (i >= key_size - 1)) {
            key[0] = '\0';
            value[0] = '\0';
            return;
        }
        p++;
        i++;
    }

    while ((*p == ':') || (*p == ' '))
        p++;

    i = 0;
    while (1) {
        value[i] = *p;
        if ((*p == '\n') || (*p == '\0') || (i >= value_size - 1)) {
            value[i] = '\0';
            break;
        }
        p++;
        i++;
    }
}

static void discardLeft(clientRequestTask *t) {
    int n;
    while (1) {
        n = read(t->fd, t->line, sizeof(t->line));
        if (0 >= n) {
            break;
        }
    }
}

/* 输出数据 */
static void _htprintf(clientRequestTask *t, const char *format, ...)
{
    if (t->lw > 0) {
        va_list ap;
        va_start(ap, format);
        t->lw = vdprintf(t->fd, format, ap);
        va_end(ap);
    }
}

static char* httpStatusText(int status) {
    int i = 0;
    int len = sizeof(http_status_list) / sizeof(http_status_list[0]);
    for (; i < len; i++) {
        if (http_status_list[i].code == status) {
            return http_status_list[i].text;
        }
    }
    return "Unknown";
}

void httpResponse(clientRequestTask *t, int status, const char* content, const char* contentType, size_t contentLength)
{
    _htprintf(t, "HTTP/1.1 %d %s\r\n", status, httpStatusText(status));
    _htprintf(t, "Server: xsync/%s\r\n", XSYNC_VERSION);
    _htprintf(t, "Content-Type: %s\r\n", (contentType ? contentType : "text/plain"));
    _htprintf(t, "Content-Length: %d\r\n", contentLength);

    char* headers = getHeadersConfig();
    if (headers) {
        _htprintf(t, "%s", headers);
    }
    
    _htprintf(t, "Connection: close\r\n\r\n");

    if (content) {
        _htprintf(t, "%s", content);
    }
}

static int getLine(clientRequestTask *t)
{
    int i = 0;
    char c = '\0';
    int fd = t->fd;

    int n, size = sizeof(t->line);
    while ((i < size - 1) && (c != '\n'))
    {
        n = recv(fd, &c, 1, 0);
        if (-1 == n && errno == EAGAIN && t->exit == 0) {
            sem_wait(&(t->sem));
            continue;
        }
        else if (n > 0)
        {
            if (c == '\r')
            {
                n = recv(fd, &c, 1, MSG_PEEK);
                if ((n > 0) && (c == '\n'))
                    recv(fd, &c, 1, 0);
                else
                    c = '\n';
            }
            t->line[i] = c;
            i++;
        }
        else
            c = '\n';
    }
    t->line[i] = '\0';

    return i;
}

static int saveFile(clientRequestTask *t)
{
    FILE *resource = NULL;

    resource = fopen(t->path, "w");
    if (resource == NULL)
        return -1;

    int save_fd = fileno(resource);
    int result;
    char c;

    t->lr = 0;
    while (t->lr < t->contentLength) {
        result = recv(t->fd, t->line, 1024, 0);
        if (result == 0) {
            break;
        } else if (result == -1) {
            if (errno == EAGAIN && t->exit == 0) {
                sem_wait(&(t->sem));
                continue;
            }
            break;
        } else {
            t->lr += result;
            write(save_fd, t->line, result);
        }
    }

    fclose(resource);
    return t->lr;
}

#include "verbs.c"
#include "handle.c"

void cmdInit()
{
    int i;
    int num = sizeof(_httpCommandTable) / sizeof(httpCommand);

    _commands = dictCreate(&dictTypeStringKey);
    
    for (i = 0; i < num; i++) {
        dictSet(_commands, _httpCommandTable[i].name, _httpCommandTable + i);
    }

    pthread_mutex_init(&_execLock, NULL);
}

void cmdFree()
{
    pthread_mutex_destroy(&_execLock);
    dictFree(_commands);
}

